Alpha Threshold¶

NiiVue allows asymmetric positive / negative statistical thresholds. It can also make sub-threshold voxels translucent.

See https://niivue.com/demos/features/alphathreshold.html for mirror.

In [1]:
from pathlib import Path

from ipyniivue import download_dataset

BASE_API_URL = "https://niivue.com/demos/images/"
DATA_FOLDER = Path("images")

# Download data for example
download_dataset(
    BASE_API_URL,
    DATA_FOLDER,
    files=[
        "fslmean.nii.gz",
        "fslt.nii.gz",
    ],
)
fslmean.nii.gz already exists.
fslt.nii.gz already exists.
Dataset downloaded successfully to images.
In [2]:
## Import Required Libraries

from pathlib import Path

import ipywidgets as widgets
from IPython.display import display

from ipyniivue import NiiVue, ShowRender, SliceType

DATA_FOLDER = Path("images")

## Create NiiVue Instance and Load Data

# Create NiiVue instance with specific settings
v = NiiVue(
    loading_text="there are no images",
    back_color=(1, 1, 1, 1),
    show_3d_crosshair=True,
    is_colorbar=True,
    multiplanar_show_render=ShowRender.ALWAYS,
)

# Set initial configuration
v.set_radiological_convention(False)
v.set_slice_type(SliceType.MULTIPLANAR)
v.set_slice_mm(False)
v.set_interpolation(True)

# Load volumes
v.load_volumes(
    [
        {"path": DATA_FOLDER / "fslmean.nii.gz"},
        {
            "path": DATA_FOLDER / "fslt.nii.gz",
            "colormap": "warm",
            "colormap_negative": "winter",
            "cal_min": 3,
            "cal_max": 6,
            "cal_min_neg": -6,
            "cal_max_neg": -3,
        },
    ]
)

# Hide colorbar for anatomical scan
v.volumes[0].colorbar_visible = False

# Set initial overlay outline
v.overlay_outline_width = 0.25

## Create Interactive Controls

# High DPI checkbox
dpi_checkbox = widgets.Checkbox(
    value=True,
    description="High DPI",
    tooltip="Higher resolution for 'retina' displays",
)

# Negative colors checkbox
negative_checkbox = widgets.Checkbox(value=True, description="Negative Colors")

# Smooth checkbox
smooth_checkbox = widgets.Checkbox(
    value=False,
    description="Smooth",
    tooltip=(
        "Trilinear interpolation blurs data, "
        "but can change which voxels survive a threshold"
    ),
)

# World space checkbox
world_checkbox = widgets.Checkbox(value=False, description="World Space")

# Threshold sliders
neg_thresh_slider = widgets.IntSlider(
    min=1, max=50, value=30, description="-Thresh", continuous_update=True
)

neg_max_slider = widgets.IntSlider(
    min=51, max=150, value=60, description="-Max", continuous_update=True
)

pos_thresh_slider = widgets.IntSlider(
    min=1, max=50, value=1, description="+Thresh", continuous_update=True
)

pos_max_slider = widgets.IntSlider(
    min=51, max=150, value=60, description="+Max", continuous_update=True
)

# Outline width slider
outline_slider = widgets.IntSlider(
    min=0, max=4, value=1, description="Outline", continuous_update=True
)

# Alpha mode dropdown
alpha_dropdown = widgets.Dropdown(
    options=[
        ("Restrict colorbar to range", 0),
        ("Colorbar from 0, transparent subthreshold", 1),
        ("Colorbar from 0, translucent subthreshold", 2),
    ],
    value=0,
    description="Alpha Mode:",
)

# Location display
location_output = widgets.HTML(value=" ")

## Setup Event Handlers


def on_dpi_change(change):
    """Handle DPI checkbox changes."""
    v.set_high_resolution_capable(change["new"])


def on_negative_change(change):
    """Handle negative colormap checkbox changes."""
    if change["new"]:
        v.set_colormap_negative(v.volumes[1].id, "winter")
    else:
        v.set_colormap_negative(v.volumes[1].id, "")


def on_smooth_change(change):
    """Handle smooth interpolation checkbox changes."""
    v.set_interpolation(not change["new"])


def on_world_change(change):
    """Handle world space checkbox changes."""
    v.set_slice_mm(change["new"])


def on_pos_thresh_change(change):
    """Handle positive threshold slider changes."""
    v.volumes[1].cal_min = 0.1 * change["new"]


def on_neg_max_change(change):
    """Handle negative maximum slider changes."""
    v.volumes[1].cal_min_neg = -0.1 * change["new"]


def on_pos_max_change(change):
    """Handle positive maximum slider changes."""
    v.volumes[1].cal_max = 0.1 * change["new"]


def on_neg_thresh_change(change):
    """Handle negative threshold slider changes."""
    v.volumes[1].cal_max_neg = -0.1 * change["new"]


def on_outline_change(change):
    """Handle outline width slider changes."""
    v.overlay_outline_width = 0.25 * change["new"]


def on_alpha_mode_change(change):
    """Handle alpha mode dropdown changes."""
    v.volumes[1].colormap_type = change["new"]


@v.on_location_change
def handle_location_change(data):
    """Update location display when crosshair moves."""
    location_output.value = f"  {data['string']}"


# Attach event handlers
dpi_checkbox.observe(on_dpi_change, names="value")
negative_checkbox.observe(on_negative_change, names="value")
smooth_checkbox.observe(on_smooth_change, names="value")
world_checkbox.observe(on_world_change, names="value")
pos_thresh_slider.observe(on_pos_thresh_change, names="value")
neg_max_slider.observe(on_neg_max_change, names="value")
pos_max_slider.observe(on_pos_max_change, names="value")
neg_thresh_slider.observe(on_neg_thresh_change, names="value")
outline_slider.observe(on_outline_change, names="value")
alpha_dropdown.observe(on_alpha_mode_change, names="value")

# Initialize values
on_pos_thresh_change({"new": pos_thresh_slider.value})
on_pos_max_change({"new": pos_max_slider.value})
on_neg_max_change({"new": neg_max_slider.value})
on_neg_thresh_change({"new": neg_thresh_slider.value})
on_outline_change({"new": outline_slider.value})
on_alpha_mode_change({"new": alpha_dropdown.value})

## Display All

# Organize controls
controls_row1 = widgets.HBox(
    [dpi_checkbox, negative_checkbox, smooth_checkbox, world_checkbox]
)

controls_row2 = widgets.HBox([neg_thresh_slider, neg_max_slider])

controls_row3 = widgets.HBox([pos_thresh_slider, pos_max_slider])

controls_row4 = widgets.HBox([outline_slider, alpha_dropdown])

# Create main layout
controls = widgets.VBox(
    [controls_row1, controls_row2, controls_row3, controls_row4, location_output]
)

# Display everything
display(widgets.VBox([controls, v]))